package com.lock.chess.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lock.chess.Enums.GameStatusEnum;
import com.lock.chess.Enums.LevelEnum;
import com.lock.chess.Enums.MessageEnum;
import com.lock.chess.config.ResponseApi;
import com.lock.chess.controller.WebSocketServer;
import com.lock.chess.dao.GameMapper;
import com.lock.chess.dao.PlayerMapper;
import com.lock.chess.dto.*;
import com.lock.chess.model.GameRoom;
import com.lock.chess.model.Player;
import com.lock.chess.service.GameService;
import com.lock.chess.utils.SocketResponse;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.websocket.Session;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class GameServiceImpl implements GameService {
    @Resource
    private GameMapper gameMapper;
    @Resource
    private PlayerMapper playerMapper;

    @Override
    public void matchGame(Session session, String userCode) {
        if (gameMapper.selectList(new QueryWrapper<GameRoom>().eq("status", GameStatusEnum.WAITING).eq("red_user_code", userCode)).size() != 0) {
            return;
        }
        List<GameRoom> roomList = gameMapper.selectList(new QueryWrapper<GameRoom>().eq("status", GameStatusEnum.WAITING).ne("red_user_code", userCode));
        if (roomList.size() == 0) {
            GameRoom gameRoom = new GameRoom();
            gameRoom.setRedUserCode(userCode);
            gameRoom.setStatus(GameStatusEnum.WAITING);
            gameMapper.insert(gameRoom);
            try {

                JSONObject entries = new JSONObject(gameRoom);
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.WAIT.getCode(), entries), userCode);

            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            GameRoom room = roomList.get(0);
            String otherPlayer = room.getRedUserCode();
            if (!WebSocketServer.isPlayerAlive(otherPlayer)) {
                gameMapper.deleteById(room.getRoomId());
                this.matchGame(session, userCode);
                return;
            }
            room.setBlackUserCode(userCode);
            room.setStatus(GameStatusEnum.RED);
            gameMapper.updateById(room);
            try {
                room.setRedPlayer(playerMapper.selectById(room.getRedUserCode()));
                room.setBlackPlayer(playerMapper.selectById(room.getBlackUserCode()));
                JSONObject entries = new JSONObject(room);
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.START.getCode(), entries), userCode);
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.START.getCode(), entries), room.getRedUserCode());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void cancelGame(Session session, String userCode) {
        List<GameRoom> roomList = gameMapper.selectList(new QueryWrapper<GameRoom>().eq("status", GameStatusEnum.WAITING).eq("red_user_code", userCode));
        if (roomList.size() != 0) {
            gameMapper.deleteBatchIds(roomList.stream().map(GameRoom::getRoomId).collect(Collectors.toList()));
            try {
                if(WebSocketServer.isPlayerAlive(userCode)) {
                    WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.CANCEL.getCode(), null), userCode);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        List<GameRoom> redList = gameMapper.selectList(new QueryWrapper<GameRoom>().eq("red_user_code", userCode).ne("status", GameStatusEnum.WAITING).ne("status", GameStatusEnum.END));
        List<GameRoom> blackList = gameMapper.selectList(new QueryWrapper<GameRoom>().eq("black_user_code", userCode).ne("status", GameStatusEnum.WAITING).ne("status", GameStatusEnum.END));

        for(GameRoom game: redList){
            GameOverAction gameOverAction = new GameOverAction();
            gameOverAction.setFromUserCode(game.getBlackUserCode());
            gameOverAction.setToUserCode(game.getRedUserCode());
            gameOverAction.setRoomId(game.getRoomId());
            this.gameOverAction(session, gameOverAction);
        }
        for(GameRoom game: blackList){
            GameOverAction gameOverAction = new GameOverAction();
            gameOverAction.setFromUserCode(game.getRedUserCode());
            gameOverAction.setToUserCode(game.getBlackUserCode());
            gameOverAction.setRoomId(game.getRoomId());
            this.gameOverAction(session, gameOverAction);
        }
    }

    @Override
    public void gameMoveAction(Session session, GameMoveAction gameActionDto, boolean isSpecial) {
        GameRoom room = gameMapper.selectOne(new QueryWrapper<GameRoom>().eq("room_id", gameActionDto.getRoomId()));
        if (isSpecial) {
            try {
                JSONObject entries = new JSONObject(gameActionDto);
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.MOVE.getCode(), entries), gameActionDto.getToUserCode());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return;
        }
        if (room.getStatus().equals(GameStatusEnum.RED) && room.getRedUserCode().equals(gameActionDto.getFromUserCode())) {
            room.setStatus(GameStatusEnum.BLACK);
            int pkTimes = room.getPkTimes() + 1;
            room.setPkTimes(pkTimes);
            if (pkTimes % 2 == 0) {
                this.startNewRound(room.getRedUserCode(), room.getBlackUserCode());
            }
            gameMapper.updateById(room);
            try {
                JSONObject entries = new JSONObject(gameActionDto);
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.MOVE.getCode(), entries), room.getBlackUserCode());
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.NEXT_TURN.getCode(), null), room.getRedUserCode());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else if (room.getStatus().equals(GameStatusEnum.BLACK) && room.getBlackUserCode().equals(gameActionDto.getFromUserCode())) {
            room.setStatus(GameStatusEnum.RED);
            int pkTimes = room.getPkTimes() + 1;
            room.setPkTimes(pkTimes);
            if (pkTimes % 2 == 0) {
                this.startNewRound(room.getRedUserCode(), room.getBlackUserCode());
            }
            gameMapper.updateById(room);
            try {
                JSONObject entries = new JSONObject(gameActionDto);
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.MOVE.getCode(), entries), room.getRedUserCode());
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.NEXT_TURN.getCode(), null), room.getBlackUserCode());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void gameEatAction(Session session, GameEatAction gameActionDto) {
        try {
            JSONObject entries = new JSONObject(gameActionDto);
            WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.EAT.getCode(), entries), gameActionDto.getToUserCode());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void gameOverAction(Session session, GameOverAction gameActionDto) {
        GameRoom room = gameMapper.selectOne(new QueryWrapper<GameRoom>().eq("room_id", gameActionDto.getRoomId()));
        room.setStatus(GameStatusEnum.END);
        room.setWinnerUserCode(gameActionDto.getFromUserCode());
        Player winner = playerMapper.selectOne(new QueryWrapper<Player>().eq("user_code", gameActionDto.getFromUserCode()));
        Player loser = playerMapper.selectOne(new QueryWrapper<Player>().eq("user_code", gameActionDto.getToUserCode()));
        LevelEnum winLevel = LevelEnum.getNextLevel(winner.getLevel());
        winner.setLevel(winLevel);
        winner.setLevelName(winLevel.getName());
        LevelEnum loseLevel = LevelEnum.getPrevLevel(loser.getLevel());
        loser.setLevel(loseLevel);
        loser.setLevelName(loseLevel.getName());
        playerMapper.updateById(winner);
        playerMapper.updateById(loser);
        gameMapper.updateById(room);
        try {
            if(WebSocketServer.isPlayerAlive(gameActionDto.getFromUserCode())){
                WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.END.getCode(), null), gameActionDto.getFromUserCode());
            }
            if(WebSocketServer.isPlayerAlive(gameActionDto.getToUserCode())) {
                WebSocketServer.sendMessage(SocketResponse.fail(MessageEnum.END.getCode(), null), gameActionDto.getToUserCode());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void levelUp(Session session, GameLevelUpAction toBean) {
        try {
            JSONObject entries = new JSONObject(toBean);
            WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.LEVEL_UP.getCode(), entries), toBean.getToUserCode());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void starUp(Session session, GameStarUpAction toBean) {
        try {
            JSONObject entries = new JSONObject(toBean);
            WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.STAR_UP.getCode(), entries), toBean.getToUserCode());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void gameChat(Session session, GameChatDto toBean) {
        try {
            JSONObject entries = new JSONObject(toBean);
            WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.CHAT.getCode(), entries), toBean.getToUserCode());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void uploadMoney(Session session, String money, String toUserCode) {
        JSONObject entries = new JSONObject();
        entries.set("money", money);
        try {
            WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.MONEY_UPLOAD.getCode(), entries), toUserCode);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    public void startNewRound(String redUserCode, String blackUserCode) {
        try {
            WebSocketServer.sendMessage(SocketResponse.success(MessageEnum.NEW_ROUND.getCode(), null), redUserCode);
            WebSocketServer.sendMessage(SocketResponse.fail(MessageEnum.NEW_ROUND.getCode(), null), blackUserCode);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
